#pragma once

namespace dotNetInteropPlugin {
	namespace PluginDev {
		using namespace System;
		using namespace System::Collections;
		using namespace System::Collections::Generic;

 

		class UnmanAIMPOptionFrame: public IAIMP2OptionFrame
		{
		public:
			UnmanAIMPOptionFrame()
			{
				ComLinkCounterDefaultInit();
				FrameNameParam = (PWCHAR)System::Runtime::InteropServices::Marshal::StringToBSTR("").ToPointer();
			}

			virtual ~UnmanAIMPOptionFrame()
			{
				if (FrameNameParam != NULL)
				{
					System::Runtime::InteropServices::Marshal::FreeBSTR(IntPtr(FrameNameParam));
					FrameNameParam = NULL;
				}
			}

			HWND BaseWnd;
			PWCHAR FrameNameParam;
			int FramePosParam;
			UnmanagedAction* SaveInfoPtr;
			UnmanagedAction* LoadInfoPtr;

			void SetFrameName(String^ Dat)
			{
				if (FrameNameParam != NULL)
				{
					System::Runtime::InteropServices::Marshal::FreeBSTR(IntPtr(FrameNameParam));
					FrameNameParam = NULL;
				}

				FrameNameParam = (PWCHAR)System::Runtime::InteropServices::Marshal::StringToBSTR(Dat).ToPointer();
			}



			[System::Runtime::InteropServices::DllImportAttribute("user32.dll")]
			static void SetParent(HWND Ch, HWND P);

			virtual HWND WINAPI FrameCreate(HWND AParent)
			{
				SetParent(BaseWnd, AParent);
				return BaseWnd;
			}

			virtual void * WINAPI FrameData()
			{
				return NULL;
			}
			virtual int WINAPI FrameFlags()
			{
				return FramePosParam;
			}

			virtual PWCHAR WINAPI FrameName()
			{
				return FrameNameParam;
			}
			virtual void WINAPI FrameFree(HWND AWindow)
			{
				SetParent(BaseWnd, HWND_MESSAGE);
			}

			virtual void WINAPI FrameLoadConfigNotify()
			{
				if (LoadInfoPtr != NULL)
					LoadInfoPtr();
			}
			virtual void WINAPI FrameSaveConfigNotify()
			{
				if (SaveInfoPtr != NULL)
					SaveInfoPtr();
			}



		public: 
			virtual ULONG STDMETHODCALLTYPE AddRef( void) 
			{ 
				_LinkCounter++; 
				return _LinkCounter; 
			} 
			virtual ULONG STDMETHODCALLTYPE Release( void) 
			{ 
				_LinkCounter--; 
				if (_LinkCounter == 0) 
				{ 
					delete this; 
					return 0; 
				} 
				return _LinkCounter; 
			} 
			virtual HRESULT STDMETHODCALLTYPE QueryInterface( 
				/* [in] */ REFIID riid, 
				/* [iid_is][out] */ __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject) 
			{ 
				ppvObject = NULL; 
				if (riid == IID_IUnknown) 
				{ 
					*ppvObject = this; 
					return S_OK; 
				} 
				return E_NOTIMPL; 
			} 
		private: 
			int _LinkCounter; 
			void ComLinkCounterDefaultInit() 
			{ 
				_LinkCounter = 1; 
			}
		};


		//====================

		/// <summary>
		/// Represent option frames for AIMP
		/// </summary>
		public ref class MAIMPOptionFrame: public MAIMPOptionFrameBase, public System::IDisposable
		{
		public:
			/// <summary>
			/// Creates new option frame
			/// </summary>
			MAIMPOptionFrame()
			{
				inFrameName = String::Empty;
				inAIMPExt = NULL;

				MyLoadParamsDelegate = gcnew Action(this, &MAIMPOptionFrame::OnLoadParameters);
				MySaveParamsDelegate = gcnew Action(this, &MAIMPOptionFrame::OnSaveParameters);

				inOptFrm = new UnmanAIMPOptionFrame();
				inOptFrm->FramePosParam = (int)inFrameCategory;
				inOptFrm->SetFrameName(inFrameName);
				inOptFrm->BaseWnd = (HWND)ControlsContainer->Handle.ToPointer();
				inOptFrm->LoadInfoPtr = (UnmanagedAction*)(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(MyLoadParamsDelegate).ToPointer());
				inOptFrm->SaveInfoPtr = (UnmanagedAction*)(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(MySaveParamsDelegate).ToPointer());
			}

			/// <summary>
			/// Creates new option frame with particular name and category
			/// </summary>
			/// <param name="FName">Name</param>
			/// <param name="FCat">Category</param>
			MAIMPOptionFrame(String^ FName, AIMPOptionFramePosition FCat)
			{
				inFrameName = String::Empty;
				inAIMPExt = NULL;

				MyLoadParamsDelegate = gcnew Action(this, &MAIMPOptionFrame::OnLoadParameters);
				MySaveParamsDelegate = gcnew Action(this, &MAIMPOptionFrame::OnSaveParameters);

				inOptFrm = new UnmanAIMPOptionFrame();
				inOptFrm->FramePosParam = (int)inFrameCategory;
				inOptFrm->SetFrameName(inFrameName);
				inOptFrm->BaseWnd = (HWND)ControlsContainer->Handle.ToPointer();
				inOptFrm->LoadInfoPtr = (UnmanagedAction*)(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(MyLoadParamsDelegate).ToPointer());
				inOptFrm->SaveInfoPtr = (UnmanagedAction*)(System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(MySaveParamsDelegate).ToPointer());


				FrameName = FName;
				FrameCategory = FCat;
			}

			~MAIMPOptionFrame()
			{
				if (inOptFrm != NULL)
				{
					if (inAIMPExt != NULL)
						inAIMPExt->AIMP_Options_FrameRemove(inOptFrm);

					inOptFrm->LoadInfoPtr = NULL;
					inOptFrm->SaveInfoPtr = NULL;

					inOptFrm->Release();
					inOptFrm = NULL;
				}

				if (inAIMPExt != NULL)
				{
					inAIMPExt->Release();
					inAIMPExt = NULL;
				}
			}

			/// <summary>
			/// Name
			/// </summary>
			property String^ FrameName
			{
				String^ get()
				{
					return inFrameName;
				}
				void set(String^ val)
				{
					inFrameName = val;
					if (inOptFrm != NULL)
						inOptFrm->SetFrameName(val);
				}
			}

			/// <summary>
			/// Category
			/// </summary>
			property AIMPOptionFramePosition FrameCategory
			{
				AIMPOptionFramePosition get()
				{
					return inFrameCategory;
				}
				void set(AIMPOptionFramePosition val)
				{
					inFrameCategory = val;
					if (inOptFrm != NULL)
						inOptFrm->FramePosParam = (int)val;
				}
			}

			/// <summary>
			/// Notify player about changes in parameters
			/// </summary>
			void NotifyAboutChanges()
			{
				if (inAIMPExt != NULL && inOptFrm != NULL)
					inAIMPExt->AIMP_Options_ModifiedChanged(inOptFrm);
			}

			/// <summary>
			/// Is frame registered
			/// </summary>
			property Boolean IsRegistered
			{
				Boolean get()
				{
					return inAIMPExt != NULL;
				}
			}

		private:
			String^ inFrameName;
			AIMPOptionFramePosition inFrameCategory;

			IAIMP2Extended* inAIMPExt;
			UnmanAIMPOptionFrame* inOptFrm;

			Action^ MyLoadParamsDelegate;
			Action^ MySaveParamsDelegate;

		internal:
			bool RegisterFrame(IAIMP2Extended* AE)
			{
				bool Res = false;
				if (inAIMPExt == NULL && inOptFrm != NULL)
				{
					inAIMPExt = AE;
					inAIMPExt->AddRef();

					if (!inAIMPExt->AIMP_Options_FrameAdd(inOptFrm))
					{
						inAIMPExt->Release();
						inAIMPExt = NULL;
					}
					else
						Res = true;
				}
				return Res;
			}

			bool UnregisterFrame()
			{
				bool Res = false;
				if (inAIMPExt != NULL && inOptFrm != NULL)
				{
					if (inAIMPExt->AIMP_Options_FrameRemove(inOptFrm))
					{
						inAIMPExt->Release();
						inAIMPExt = NULL;
						Res = true;
					}
				}
				return Res;
			}

		};




	}
}